#include "typedef.h"
#include "bitmapapi.h"
#include "arm_common.h"

/**
 * @note https://github.com/umanovskis/baremetal-arm/blob/master/doc/07_interrupts.md
 * @note IHI0048A gic architecture specification v1.0
 */

#define RO const //Read Only
#define RW       //Read Write
#define N_       //No USE
#define WO       //Write Only

typedef volatile struct GICD{

    /**
     * @brief Control Register, 0x0
     * @note  bit0 enable if 1 else disable
     */
    word ICDDCR;
    word ICDICTR;
    word ICDIIDR;
    word reserved0[29];        /* 0xC - 0x7c; reserved and implementation-defined */
    /**
     * @brief Interrupt Security Register, 0x80~0xfc
     * @note For each bit:
     * @note 0 The corresponding interrupt is Secure.
     * @note 1 The corresponding interrupt is Non-secure
     * @note ICDISR[0] reset value is impelementation defined.
     * @note ICDISRs remaining reset value is zero
     */
    word ICDISR[32];
    /**
     * @brief Interrupt set enable, 0x100-0x17c
     * @note For SPIs and PPIs, for each bit:
     * @note Reads 0 The corresponding interrupt is disabled.
     * @note Reads 1 The corresponding interrupt is enabled.
     * @note Writes 0 Has no effect.
     * @note Writes 1 Enables the corresponding interrupt.
     * @note For SGIs the behavior of the bit on reads and writes is IMPLEMENTATION DEFINED.
     */
    word ICDISER[32];
    /**
     * @brief Interrupt Clear enable, 0x180-0x1fc
     * @note For SPIs and PPIs, for each bit:
     * @note Reads 0 The corresponding interrupt is disabled.
     * @note Reads 1 The corresponding interrupt is enabled.
     * @note Writes 0 Has no effect.
     * @note Writes 1 Enables the corresponding interrupt.
     * @note For SGIs the behavior of the bit on reads and writes is IMPLEMENTATION DEFINED.
     */
    word ICDICER[32];
    /**
     * @brief Interrupt Set Pending, 0x200-0x27c
     * @note For each bit:
     * @note Reads 0 The corresponding interrupt is not pending on any cpu.
     * @note Reads 1 The corresponding interrupt is pending on at least 1 cpu for SPI.
     * @note Reads 1 The corresponding interrupt is pending on at least 1 cpu for SGI and PPI.
     * @note Writes 0 Has no effect.
     * @note Writes 1 for Edge-triggered:
     * @note pending if inactive,
     * @note active and pending if active,
     * @note pending if pending.
     * @note Writes 1 for Level-triggered
     */
    word ICDISPR[32];

    /**
     * @brief Interrupt Clear Pending, 0x280-0x2fc
     * @note For each bit:
     * @note Reads 0 The corresponding interrupt is not pending on any cpu.
     * @note Reads 1 The corresponding interrupt is pending on at least 1 cpu for SPI.
     * @note Reads 1 The corresponding interrupt is pending on at least 1 cpu for SGI and PPI.
     * @note Writes 0 Has no effect.
     * @note Writes 1 for Edge-triggered:
     * @note pending if inactive,
     * @note active and pending if active,
     * @note pending if pending.
     * @note Writes 1 for Level-triggered
     */
    word ICDICPR[32];

    /**
     * @brief Active Bit, 0x300-0x37c
     * @note For each bit:
     * @note 0 Corresponding interrupt is not active.
     * @note 1 Corresponding interrupt is active.
     * @note active means active and pending status
     */
    word ICDABR[32];
    word reserved1[32];

    /**
     * @brief Interrupt Priority, 0x400-0x7f8
     *
     * @note The ICDIPRs provide an 8-bit Priority field for each interupt supported by the GIC. 
     * @note This field stores the priority of the corresponding interrupt.
     * @note each byte for an interrupt
     * @note bit31-bit24 priority3
     * @note bit23-bit16 priority2
     * @note bit15-bit8 priority1
     * @note bit7-bit0 priority0
     */
    word ICDIPR[255];
    word reserved2;   /*0x7fc*/

    /**
     * @brief Interrupt Processor Target, 0x800-0xbf8
     * @note The ICDIPTRs provide an 8-bit CPU targets field for each interrupt supported by 
     * @note the GIC. This field stores the list of processors that the interrupt is sent to 
     * @note if it isasserted.
     * @note each bit means a cpu interface will accept the interrupt 0bxxx_xxx_x1 means cpu 0
     * @note bit31-bit24 target 3
     * @note bit23-bit16 target 2
     * @note bit15-bit8 target 1
     * @note bit7-bit0 target 0
     */
    RO word ICDIPTR[255];
    N_ word reserved3; /*0xbfc*/

    /**
     * @brief Interrupt Configuration, 0xc00-0xcfc
     * 
     */
    RW word ICDICFR[64];
    N_ word not_used3[64]; /*0xd00-0xdfc*/
    N_ word reserved4[64]; /*0xe00-0xefc*/

    /**
     * @brief Software Generated Interrput, 0xf00
     * @note bit3-0 sgi int id
     * @note bit23-16 cpu target list, only used when filter=00
     * @note bit25-24 target list filter, 00 speficied by target list, 01 except self, 10 self
     */
    WO word ICDSGIR;

    /**
     * registers undeer this address is reserved on gic v1.o
     * ot implemented definited or reserved.
     * 0xf04-0xfcc is reserved.
     * 0xfd0-0xffc is implemention defined RO.
     */

} *GICD;


typedef volatile struct __attribute__((packed)) GICC {
    /**
     * @brief CPU Interface
     * @note enabling the signalling of interrupt requests by the CPU interface
     * @note acknowledging an interrupt
     * @note indicating completion of the processing of an interrupt
     * @note setting an interrupt priority mask for the processor
     * @note defining the preemption policy for the processor
     * @note determining the highest priority pending interrupt for the processor
     */
    /**
     * @brief Control Register, 0x0
     * @note  bit0 enable if 1 else disable
     */
    RW word ICCICR;
    /**
     * @brief Priority Mask, 0x4
     * @note  bit7-0  the priority
     * @note  Higher priority corresponds to a lower Priority field value
     */
    RW word ICCPMR;

    /**
     * @brief Binary Point, 0x8
     * @note The register defines the point at which the priority value fields split into two parts.
     * @note the group priority field and the subpriority field. 
     * @note bit2-0 the point
     * @note priority=0b11100001, bp=3, gp.subp=0b1110.001
     */
    RW word ICCBPR;            /* 0x8 Binary point register */

    /**
     * @brief Interrupt Acknowledge, 0xc, RO
     * @note  bit12-10 cpuid,  For SGIs in a multiprocessor implementation, 
     *                          this field identifies the processor that 
     *                          requested the interrupt.
     * @note  bit9-0 intid
     */
    RO word ICCIAR;

    /**
     * @brief End of Interrupt, 0x10, WO
     * @note  A processor writes to this register to inform the CPU interface that
     *        it has completed its interrupt service routine for the specified interrupt.
     * @note  bit12-10 cpuid,  For SGIs in a multiprocessor implementation, 
     *                          this field identifies the processor that 
     *                          requested the interrupt.
     * @note  bit9-0 intid
     */
    RW word ICCEOIR;

    /**
     * @brief Running Priority, 0x14, RO
     * @note Indicates the priority of the highest priority interrupt that is active on the CPU  interface.
     * @note  bit7-0  the priority
     * @note  Higher priority corresponds to a lower Priority field value
     */
    RO word ICCRPR;

    /**
     * @brief Highest Pending Interrupt, 0x18
     * @note Indicates the Interrupt ID, and processor ID if appropriate,
     *       of the pending interrupt with the highest priority on the interface
     * @note  bit12-10 cpuid,  For SGIs in a multiprocessor implementation, 
     *                          this field identifies the processor that 
     *                          requested the interrupt.
     * @note  bit9-0 intid
     */
    RO word ICCHPIR;
    /**
     * registers undeer this address is reserved on gic v1.o
     * ot implemented definited including ICCIDR.
     * ICCIDR means an ID register of cpu interface, for an
     * arm implementation the value is 0x390 for upper 12
     * bits and 0x43b for lower 12 bits.
     */
} *GICC;


static GICD gicd;
static GICC gicc;

void gicc_init()
{
    /**
     * @brief Initialization after reset
     * GICC and GICD are both disabled after reset.
     * SPI: 32-1019
     * 1. write ICDISRs to specify which interrupts are Non-secure
     * 2. write ICDICFR to specify level-sensitive or edge-triggered
     * 3. write ICDIPR to specify priority
     * 4. wirte ICDIPTR to specify the target processor to handle the int
     * 5. write ICDISER to enable interrupts
     *
     * PPI: 0-15, 16-31
     * 1. write ICDISR0 to speicify which interrupts are Non-secure
     * 2. for 15-31, wirte ICDICFR to specify level or edge trigger
     * 3. write ICDIPR to specify priority
     * 4. write ICDISER0 to enable PPIs
     *
     * for the GICD:
     * 1. write ICDDCR to enable GICD.
     *
     * foe each GICC:
     * 1. write ICCPMR to set the lowest priority the gicc will accept
     * 2. write ICCBPR to set the binary point position to determins preemption
     * 3. write ICCICR to to enable GICC
     */
    address base = get_pheriphal_addr();
    gicc = CAST_AS(GICC, base + 0x100);
    word id = gicc->ICCIAR;
    gicc->ICCPMR = 0xff;
    gicc->ICCICR = 1;
    gicc->ICCEOIR = id;
}

void gicd_init()
{
    address base = get_pheriphal_addr();
    gicd = CAST_AS(GICD, base + 0x1000);
    gicd->ICDDCR = 1;
}

void gic_enable_interrupt(word id, word target)
{
    // enable the int
    SET_REG_BIT(gicd->ICDISER, id);
    // SET_REG_BYTE(gicd->ICDIPTR, id, 1 << (target & 0b11));
    SET_REG_BYTE(gicd->ICDIPR, id, 0x80);
}

void gic_send_ici(int id, word target, word filter)
{
    gicd->ICDSGIR = (filter & 0b11) << 24 |
                    (target & 0xff) << 16 | (id & 0b1111);
}